﻿using NServiceKit.Redis;
using NServiceKit.Redis.Generic;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace HobSoft.SoccerBet2014.Domain.Implementation
{
    using Core;
    using Domain.Models.Core;
    using SoccerBet2014.Common.Errors;
    using SoccerBet2014.Common.Queryable;

    public abstract class ReadonlyRedisRepository<T> : IReadonlyRepository<T>, IUniKeyReadRepository<T>
        where T : class, IModel
    {

        public ReadonlyRedisRepository(PooledRedisClientManager _redisPoolManager)
        {
            if (_redisPoolManager == null)
                throw new ArgumentNullException("_redisPoolManager", "need _redisPoolManager.");
            redisClientManager = _redisPoolManager;
        }

        protected readonly PooledRedisClientManager redisClientManager;

        #region protected method
        protected virtual IRedisClient CreateReadRedisClient()
        {
            return redisClientManager.GetReadOnlyClient();
        }

        protected virtual IRedisTypedClient<TR> CreateReadRedisClient<TR>()
        {
            return redisClientManager.GetReadOnlyClient().As<TR>();
        }

        protected virtual TEnt GetModelById<TEnt>(object id)
        {
            using (var redisClient = redisClientManager.GetReadOnlyClient())
            {
                using (var redisModel = redisClient.As<TEnt>())
                {
                    return redisModel.GetById(id);
                }
            }
        }

        protected virtual IList<TEnt> GetModelsById<TEnt>(IEnumerable<object> ids)
        {
            using (var redisClient = redisClientManager.GetReadOnlyClient())
            {
                using (var redisModel = redisClient.As<TEnt>())
                {
                    return redisModel.GetByIds(ids);
                }
            }
        }
        #endregion

        #region IReadonlyRepository
        public virtual T Get(params object[] id)
        {
            if (id.Length < 1) throw new ArgumentNullException("need id");
            return GetModelById<T>(id[0]);
        }

        public virtual IList<T> GetMany(Expression<Func<T, bool>> predicate)
        {
            predicate.IsNull().ThrowNull("predicate", "need predicate.");

            using (var redisClient = CreateReadRedisClient())
            {
                using (var redisModel = redisClient.As<T>())
                {
                    return redisModel.GetAll().AsQueryable().Where(predicate).ToList();
                }
            }
        }

        public virtual IList<T> GetMany(Expression<Func<T, bool>> predicate, params ISortExpression<T>[] sortExpression)
        {
            predicate.IsNull().ThrowNull("predicate", "need predicate.");
            sortExpression.IsNull().ThrowNull("sortExpression", "need sortExpression.");
            
            using (var redisClient = CreateReadRedisClient())
            {
                using (var redisModel = redisClient.As<T>())
                {
                    return redisModel.GetAll().AsQueryable().Where(predicate).Sort(sortExpression).ToList();
                }
            }
        }
        #endregion

        #region IUniKeyReadRepository
        public virtual IList<T> GetMany(ISortExpression<T>[] sortExpression, params object[] keys)
        {
            sortExpression.IsNull().ThrowNull("sortExpression", "need sortExpression.");
            using (var redisClient = CreateReadRedisClient())
            {
                using (var redisModel = redisClient.As<T>())
                {
                    return redisModel.GetByIds(keys).AsQueryable().Sort(sortExpression).ToList();
                }
            }
        }

        public virtual IList<T> GetMany(params object[] keys)
        {
            keys.IsNull().ThrowNull("keys", "need keys");
            if (!keys.Any())
                return new List<T>();
            return GetModelsById<T>(keys);
        }
        #endregion
    }
}
